﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Newtonsoft.Json;
using LuDragon.Common.Enums;
using YeaJur.Mapper;

namespace LuDragon.Common.Extensions
{
    public class ExpressionExtension<T> where T : class
    {
        public static Expression<Func<T, bool>> CreateExpression(string queryParams)
        {
            var jParams = queryParams.ToModel<Dictionary<string,object>>();
            ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
            Expression query = Expression.Constant(true);
            foreach (var item in jParams)
            {
                if (string.IsNullOrEmpty(item.Value.ToString())) continue;
                MemberExpression memberExpression = Expression.PropertyOrField(parameter, item.Key);

                if (memberExpression.Type.Name == nameof(String))
                {
                    var containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });

                    query = Expression.And(query, Expression.Call(memberExpression, containsMethod, Expression.Constant(item.Value.ToString())));
                }
                else if (memberExpression.Type.IsEnum)
                {
                    //枚举
                    var v = Int32.Parse(item.Value.ToString());
                    var value = Enum.ToObject(memberExpression.Type, v);
                    Expression.And(query, Expression.Equal(memberExpression, Expression.Constant(value, memberExpression.Type)));
                }
                else
                { 

                    Expression.And(query, Expression.Equal(memberExpression, Expression.Constant(Convert.ChangeType(item.Value, memberExpression.Type))));
                }
            }
            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }

        public static Expression<Func<T, bool>> ExpressionBuilder(string queryParams)
        {
            List<WhereFilter> filters = JsonConvert.DeserializeObject<List<WhereFilter>>(queryParams);
            ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
            Expression query = Expression.Constant(true);
            for (int i = 0; i < filters.Count; i++)
            {
                MemberExpression propertyAccess = Expression.PropertyOrField(parameter, filters[i].Field);
                ConstantExpression constExpression;
                //时间处理
                DateTime dt;
                if (DateTime.TryParse(filters[i].Value.ToString(), out dt))
                {
                    constExpression = Expression.Constant(dt, typeof(DateTime));
                    //constExpression = Expression.Constant(dt, typeof(DateTime?));
                    //注：Datetime与Datetime?不能作比较
                }
                else
                {//非时间格式
                    constExpression = Expression.Constant(filters[i].Value);
                }
                Expression comparisionExpression = Expression.Constant(true);
                switch (filters[i].Operation)
                {
                    case Operation.Equal:
                        comparisionExpression = Expression.Equal(propertyAccess, constExpression);
                        break;
                    case Operation.NotEqual:
                        comparisionExpression = Expression.NotEqual(propertyAccess, constExpression);
                        break;
                    case Operation.GreaterThan:
                        comparisionExpression = Expression.GreaterThan(propertyAccess, constExpression);
                        break;
                    case Operation.GreaterThanOrEqual:
                        comparisionExpression = Expression.GreaterThanOrEqual(propertyAccess, constExpression);
                        break;
                    case Operation.LessThan:
                        comparisionExpression = Expression.LessThan(propertyAccess, constExpression);
                        break;
                    case Operation.LessThanOrEqual:
                        comparisionExpression = Expression.LessThanOrEqual(propertyAccess, constExpression);
                        break;
                    case Operation.Like:
                        var containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                        comparisionExpression = Expression.Call(propertyAccess, containsMethod, constExpression);
                        break;
                    case Operation.StartsWith:
                        var startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
                        comparisionExpression = Expression.Call(propertyAccess, startsWithMethod, constExpression);
                        break;
                    case Operation.EndWith:
                        var endWithMethod = typeof(string).GetMethod("EndWith", new Type[] { typeof(string) });
                        comparisionExpression = Expression.Call(propertyAccess, endWithMethod, constExpression);
                        break;
                    default:
                        comparisionExpression = Expression.Equal(propertyAccess, constExpression);
                        break;
                }
                query = Expression.And(query, comparisionExpression);
            }
            return Expression.Lambda<Func<T, bool>>(query, parameter);
        }
    }
}
